//
// Created by Key-Box-Min on 11/07/2020.
//

#ifndef NETBASE_H
#define NETBASE_H

#include <iostream>
#include <string>
#include <cstring>

#if __linux
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/epoll.h>
#include <syscall.h>
#endif

#if WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <WinSock2.h>
#include <inaddr.h>
#include <ws2tcpip.h>
#include <Mstcpip.h>
#endif

namespace NetBase{

    //Net Base ================================================================================

    namespace Base {
        int net_socket(int __af,int __type,int __protocol);
        int net_close(int sock);

        int net_bind (int __fd, const sockaddr* __addr, int __len);
        int net_accept(int __fd, sockaddr* __addr,uint32_t * __addr_len = nullptr);
        int net_connect(int __fd, const sockaddr* __addr, int __len);

        int net_send (int __fd, const int8_t*__buf, uint32_t __n, int __flags);
        int net_recv (int __fd, int8_t*__buf, uint32_t __n, int __flags);

        int net_sendto (int __fd, const int8_t*__buf, uint32_t __n,int __flags,const sockaddr* __addr,uint32_t __addr_len);
        int net_recvfrom (int __fd, int8_t* __buf, uint32_t __n,int __flags,sockaddr* __addr,uint32_t * __addr_len);

        class InitNetEnvironmental{
        public:
            InitNetEnvironmental();
            virtual ~InitNetEnvironmental();
        };
    }


    struct NetAddress{
    public:
        NetAddress(){}
        NetAddress(sockaddr addr):__addr(addr){}
        NetAddress(std::string addr,int port);
        NetAddress(uint32_t addr,int port);

        sockaddr& getAddr() {return __addr;}
        const sockaddr& getAddr() const {return __addr;}

        sockaddr_in& getAddrIn(){return *(sockaddr_in*)(&__addr);}
        const sockaddr_in& getAddrIn() const {return *(sockaddr_in*)(&__addr);}

        std::string getIp() const {return std::string(inet_ntoa(getAddrIn().sin_addr));}
        char* getIp_Char() const {return inet_ntoa(getAddrIn().sin_addr);}
        uint32_t getPort() const {return getAddrIn().sin_port;}

        friend bool operator < (const NetAddress& a,const NetAddress& b){
            if(a.getIp() < b.getIp())return true;
            return a.getPort() < b.getPort();
        }
        friend bool operator > (const NetAddress& a,const NetAddress& b){
            if(a.getIp() > b.getIp())return true;
            return a.getPort() > b.getPort();
        }
    protected:
        sockaddr __addr;
    };

    //TCP / UDP ================================================================================

    class NetBaseAbstract{
    public:
        NetBaseAbstract():__sock(-1){}
        NetBaseAbstract(int sock):__sock(sock){}

        int getSocket(){return __sock;}
        void setSocket(const int& socket){__sock = socket;}
        NetAddress& getAddr(){return __addr;}
        void close(){Base::net_close(__sock);}

    protected:
        int __sock;
        NetAddress __addr;
    };


    class NetTcp : public NetBaseAbstract{
    public:
        NetTcp();
        NetTcp(int sock);

        bool bind(const NetAddress& addr,int count);
        NetTcp* accept();
        int     accept(sockaddr& addr);
        bool connect(const NetAddress& addr);

        int send(const int8_t* data,const uint32_t& len,int flags=0);
        int recv(int8_t* data,const uint32_t& len,int flags=0);
    protected:
        int __count;
    };


    class NetUdp : public NetBaseAbstract{
    public:
        NetUdp();
        NetUdp(int sock);

        bool bind(const NetAddress& addr);
        int send(const NetAddress& addr,const int8_t* data,const uint32_t& len,int flags=0);
        int recv(NetAddress& addr,int8_t* data,const uint32_t& len,int flags=0);
    };

    //Net Test ================================================================================

    namespace Test{
        void netBase_Tcp_Client(NetAddress addr,std::string msg,int count = -1);
        void netBase_Udp_Client(NetAddress addr,std::string msg,int count = -1);
    }

}


#endif // NETBASE_H
